package com.asiainfo.rsa;

import java.io.File;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.apache.commons.codec.binary.Base64;

/**
 * Created by ThinkPad on 2017/12/18.
 */
public class RSAEncrypt {

    public static final String PUBLIC_KEY_NAME = "publicKey.keystore";

    public static final String PRIVATE_KEY_NAME = "privateKey.keystore";

    public static void genKeyPair(String filePath) {
        KeyPairGenerator keyPairGen = null;
        try {
            keyPairGen = KeyPairGenerator.getInstance("RSA");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        // 初始化密钥对生成器，密钥大小为96-1024位
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 得到私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();

        // 得到公钥字符串
        String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
        // 得到私钥字符串
        String privateKeyString = Base64.encodeBase64String(privateKey.getEncoded());
        //生成公钥文件
        IOUtil.writeStrToFile(publicKeyString, filePath + File.separator + PUBLIC_KEY_NAME);
        //生成私钥文件
        IOUtil.writeStrToFile(privateKeyString, filePath + File.separator + PRIVATE_KEY_NAME);
    }


    public static RSAPublicKey loadPublicKeyByStr(String publicKeyPath)
        throws Exception {
        try {
            String publicKeyStr = IOUtil.loadKeyByFile(publicKeyPath);
            byte[] buffer = Base64.decodeBase64(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            e.printStackTrace();
            throw new Exception("公钥数据为空");
        }
    }

    public static RSAPrivateKey loadPrivateKeyByStr(String privateKeyPath)
        throws Exception {
        try {
            String privateKeyStr = IOUtil.loadKeyByFile(privateKeyPath);
            byte[] buffer = Base64.decodeBase64(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            e.printStackTrace();
            throw new Exception("私钥数据为空");
        }
    }

    public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData)
        throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] output = cipher.doFinal(plainTextData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
        return null;
    }

    public static byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData)
        throws Exception {
        if (privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] output = cipher.doFinal(cipherData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("解密私钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("密文数据已损坏");
        }
    }

    public static void main(String[] args) throws Exception {
        String path = "E:\\abc";
        File publicKeyFile = new File(path + File.separator + PUBLIC_KEY_NAME);
        File privateKeyFile = new File(path + File.separator + PRIVATE_KEY_NAME);
        if(!publicKeyFile.exists() && !privateKeyFile.exists()){
            genKeyPair(path);
        }
        String content = "hello,world!!!!";
        System.out.println("原数据：" + content);
        RSAPublicKey publicKey = loadPublicKeyByStr(path + File.separator + PUBLIC_KEY_NAME);
        byte[] cipherData = encrypt(publicKey,content.getBytes());
        System.out.println("加密后数据：" + Base64.encodeBase64String(cipherData));
        RSAPrivateKey privateKey = loadPrivateKeyByStr(path + File.separator + PRIVATE_KEY_NAME);
        byte[] res = decrypt(privateKey,cipherData);
        System.out.println("解密后数据：" + new String(res));
    }
}
